Estructura basada en mensajes de windows, hace más tareas que un programa no WinAPI.
#include <windows.h> // Obligatoria para WinAPI
// Prototipos:
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
// Función de entrada:
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdParam,
int nCmdShow)
{
// Declaración:
// Inicialización:
// Bucle de mensajes:
return Message.wParam;
}#include <windows.h> // Obligatoria para WinAPI
// Prototipos:
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
// Función de entrada:
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdParam,
int nCmdShow)
{
// Declaración:
// Inicialización:
// Bucle de mensajes:
return Message.wParam;
}Cada tipo (o clase) de ventana que usemos en nuestro programa (normalmente sólo será una), o cada cuadro de diálogo (de estos puede haber muchos), necesitará un procedimiento propio, que deberemos declarar y definir. Siguiendo la estructura de un programa C, esta es la zona normal de declaración de prototipos.
La función de entrada de un programa Windows es WinMain, en vez de main. Normalmente, la definición de esta función cambia muy poco de una aplicaciones a otras. Se divide en tres partes claramente diferenciadas: Declaración, inicialización y bucle de mensajes.
hInstance es un manipulador para la instancia del programa que estamos ejecutando. Cada vez que se ejecuta una aplicación, Windows crea una Instancia para ella, y le pasa un manipulador de dicha instancia a la aplicación.
hPrevInstance es un manipulador a instancias previas de la misma aplicación. Como Windows es multitarea, pueden existir varias versiones de la misma aplicación ejecutándose, varias instancias. En Windows 3.1, este parámetro nos servía para saber si nuestra aplicación ya se estaba ejecutando, y de ese modo se podían compartir los datos comunes a todas las instancias. Pero eso era antes, ya que en Win32 usa un segmento distinto para cada instancia y este parámetro es siempre NULL, sólo se conserva por motivos de compatibilidad.
lpszCmdParam esta cadena contiene los argumentos de entrada del comando de línea.
nCmdShow este parámetro especifica cómo se mostrará la ventana. Para ver sus posibles valores consultar valores de ncmdshow. Se recomienda no usar este parámetro en la función ShowWindow la primera vez que se ésta es llamada. En su lugar debe usarse el valor SW_SHOWDEFAULT
HWND hWnd un manipulador para la ventana principal de la aplicación. Ya sabemos que nuestra aplicación necesitará al menos una ventana.
MSG Message una variable para manipular los mensajes que lleguen a nuestra aplicación.
WNDCLASSEX wincl una estructura que se usará para registrar la clase particular de ventana que usaremos en nuestra aplicación. Existe otra estructura para registrar clases que se usaba antiguamente, pero que ha sido desplazada por esta nueva versión, se trata de WNDCLASS.
Esta zona se encarga de registrar la clase o clases de ventana, crear la ventana y visualizarla en pantalla.
Para registrar la clase primero hay que rellenar adecuadamente la estructura WNDCLASSEX, que define algunas características que serán comunes a todas las ventanas de una misma clase, como color de fondo, icono, menú por defecto, el procedimiento de ventana, etc. Después hay que llamar a la función RegisterClassEx.
En el caso de usar una estructura WNDCLASS se debe registrar la clase usando la función RegisterClass.
A continuación se crea la ventana usando la función CreateWindowEx, la función CreateWindow ha caído prácticamente en desuso. Cualquiera de estas dos funciones nos devuelve un manipulador de ventana que podemos necesitar en otras funciones, sin ir más lejos, la siguiente.
Para que la ventana sea visible en pantalla hay que llamar a la función ShowWindow. La primera vez que se llama a ésta función, después de crear la ventana, se puede usar el parámetro nCmdShow de WinMain como parámetro o mejor aún, como se recomienda por Windows, el valor SW_SHOWDEFAULT.
Este es el núcleo de la aplicación, el programa permanece en este bucle mientras la función GetMessage retorne con un valor TRUE. Aunque puede retornar TRUE, FALSE o -1 (error).
/*while(GetMessage(&mensaje, 0, 0, 0)) ver NOTA */
while(TRUE == GetMessage(&mensaje, 0, 0, 0)) /* optimizado */
{
TranslateMessage(&mensaje);
DispatchMessage(&mensaje);
}/*while(GetMessage(&mensaje, 0, 0, 0)) ver NOTA */
while(TRUE == GetMessage(&mensaje, 0, 0, 0)) /* optimizado */
{
TranslateMessage(&mensaje);
DispatchMessage(&mensaje);
}NOTA: El código que se suele usar es el que no está comentado, el problema con es que si GetMessage regresa un valor -1, que indica un error, la condición del “while” se considera verdadera, y el bucle continúa. Si el error es permanente, el programa jamás terminará.
La función TranslateMessage se usa para traducir los mensajes de teclas virtuales a mensajes de carácter. Veremos esto con más detalle en el capítulo dedicado al teclado (cap. 34).
Los mensajes traducidos se reenvían a la lista de mensajes del proceso, y se recuperarán con las siguientes llamadas a GetMessage.
La función DispatchMessage envía el mensaje al procedimiento de ventana, donde será tratado adecuadamente. El próximo capítulo está dedicado al procedimiento de ventana, y al final de él estaremos en disposición de crear nuestro primer programa Windows.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
/* Declaración: */
HWND hwnd;
MSG mensaje;
WNDCLASSEX wincl;
/* Inicialización: */
/* Estructura de la ventana */
wincl.hInstance = hInstance;
wincl.lpszClassName = "NUESTRA_CLASE";
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
/* Usar icono y puntero por defecto */
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
/* Registrar la clase de ventana, si falla, salir del programa */
if(!RegisterClassEx(&wincl)) return 0;
hwnd = CreateWindowEx(
0,
"NUESTRA_CLASE",
"Ejemplo 001",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
544,
375,
HWND_DESKTOP,
NULL,
hThisInstance,
NULL
);
ShowWindow(hwnd, SW_SHOWDEFAULT);
/* Bucle de mensajes: */
while(TRUE == GetMessage(&mensaje, 0, 0, 0))
{
TranslateMessage(&mensaje);
DispatchMessage(&mensaje);
}
return mensaje.wParam;
}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
/* Declaración: */
HWND hwnd;
MSG mensaje;
WNDCLASSEX wincl;
/* Inicialización: */
/* Estructura de la ventana */
wincl.hInstance = hInstance;
wincl.lpszClassName = "NUESTRA_CLASE";
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
/* Usar icono y puntero por defecto */
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
/* Registrar la clase de ventana, si falla, salir del programa */
if(!RegisterClassEx(&wincl)) return 0;
hwnd = CreateWindowEx(
0,
"NUESTRA_CLASE",
"Ejemplo 001",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
544,
375,
HWND_DESKTOP,
NULL,
hThisInstance,
NULL
);
ShowWindow(hwnd, SW_SHOWDEFAULT);
/* Bucle de mensajes: */
while(TRUE == GetMessage(&mensaje, 0, 0, 0))
{
TranslateMessage(&mensaje);
DispatchMessage(&mensaje);
}
return mensaje.wParam;
}